const Group = require('../models/Group');
const Notice = require('../models/Notice');
const Bill = require('../models/Bill');
const InviteCode = require('../models/InviteCode');

const { StatusCodes } = require('http-status-codes');
const mongoose = require('mongoose');
const _ = require('lodash');
const { makeInvitations } = require('../utils/doc');
const { genCode } = require('../utils/common');

// 获取与自己相关的分组
const getGroups = async (req, res) => {
   const userId = mongoose.Types.ObjectId(req.user.userId);
   let groups = await Group.aggregate([
      {
         $match: {
            $or: [{ createdBy: userId }, { members: { $in: [userId] } }],
         },
      },
   ]);

   res.status(StatusCodes.OK).json({
      data: {
         list: groups,
      },
   });
};

// 创建分组，成员固定包含自己
const createGroup = async (req, res) => {
   const userId = req.user.userId;
   let { members, ...rest } = req.body;

   const session = await mongoose.startSession();
   session.startTransaction();

   try {
      const group = await Group.create({
         ...rest,
         createdBy: userId,
         members: [userId],
      });
      members = members.filter((id) => id !== userId); // 排除掉自己
      await makeInvitations(group._id, userId, members);
      await session.commitTransaction();
      res.status(StatusCodes.OK).json({ msg: '创建成功' });
   } catch (err) {
      await session.abortTransaction();
      res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({ msg: err });
   } finally {
      session.endSession();
   }
};

/**
 * 获取单个分组信息
 */
const getGroup = async (req, res) => {
   const groupId = mongoose.Types.ObjectId(req.params.id);
   const group = await Group.findById(groupId).populate({
      path: 'members', // 指定要填充的字段
      select: '_id username', // 指定要返回的字段
      model: 'User', // 指定要使用的模型
   });

   const bills = await Bill.find({
      fromGroup: req.params.id,
   });

   let spend = bills
      .map((item) => item.account)
      .reduce((acc, cur) => acc + cur, 0);

   res.status(StatusCodes.OK).json({
      data: group
         ? {
              ...group.toObject(),
              spend,
           }
         : null,
   });
};

/**
 * 更新分组信息
 */
const updateGroup = async (req, res) => {
   const userId = req.user.userId;

   // 校验
   // 1. 查找旧分组，取出要被删除的成员
   const groupId = req.params.id;
   const oldGroup = await Group.findById(mongoose.Types.ObjectId(groupId));
   const oldMembersList = oldGroup.members.map((item) => item.toString());
   const deletedMembers = _.difference(oldMembersList, req.body.members);

   // 2. 如果有，从bills中查找相关账单，如果包含被删除成员， 不允许删除
   if (deletedMembers.length) {
      let bills = await Bill.find({
         fromGroup: groupId,
         members: {
            $elemMatch: {
               $in: deletedMembers,
            },
         },
      });

      if (bills.length !== 0) {
         res.status(StatusCodes.BAD_REQUEST).json({
            msg: `删除成员失败，分组账单中已关联该成员`,
         });
      }
   }

   const session = await mongoose.startSession();
   session.startTransaction();

   try {
      // 1. 更新分组
      let { group_name, budget, desc } = req.body;

      await Group.findByIdAndUpdate(
         mongoose.Types.ObjectId(groupId),
         {
            group_name,
            budget,
            desc,
         },
         {
            runValidators: true,
         }
      );

      // 2. 发送通知
      // 邀请通知
      const addMembers = _.difference(req.body.members, [
         ...oldMembersList,
         userId,
      ]);
      await makeInvitations(groupId, userId, addMembers);

      // 踢人通知
      if (deletedMembers.length) {
         await Notice.insertMany([
            // 1. 通知别人被踢了
            ...deletedMembers.map((id) => ({
               type: 'INFORMED_KICK_OUT_MEMBER',
               group: groupId,
               receiver: id,
            })),
            // 2. 踢人记录
            {
               type: 'KICK_OUT_MEMBER',
               group: groupId,
               receivers: members,
               sender: userId,
            },
         ]);
      }
      await session.commitTransaction();
      res.status(StatusCodes.OK).json({ msg: '更新成功' });
   } catch (err) {
      await session.abortTransaction();
      res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({ msg: err });
   } finally {
      session.endSession();
   }
};

/**
 * 删除分组
 */
const removeGroup = async (req, res) => {
   const groupId = mongoose.Types.ObjectId(req.params.id);
   const bills = await Bill.find({ fromGroup: groupId });
   if (bills.length !== 0) {
      res.status(StatusCodes.BAD_REQUEST).json({
         msg: '该分组存在账单，无法删除',
      });
   }

   await Group.findByIdAndDelete(groupId);
   res.status(StatusCodes.OK).json({ msg: '删除成功' });
};

const genIntiveCode = async (req, res) => {
   const groupId = req.body.groupId;
   const code = genCode();
   const results = await InviteCode.find({
      groupId: mongoose.Types.ObjectId(groupId),
   });

   if (results.length === 0) {
      await InviteCode.create({
         groupId,
         code,
      });
   } else {
      await InviteCode.updateOne(
         { groupId },
         {
            code,
         }
      );
   }

   res.status(StatusCodes.OK).json({
      msg: 'ok',
      data: {
         code,
      },
   });
};

const joinGroupByCode = async (req, res) => {
   if (Number.isNaN(req.body.code) || req.body.code.length !== 6) {
      return res.status(StatusCodes.BAD_REQUEST).json({
         msg: '请输入正确的邀请码',
      });
   }
   const result = await InviteCode.findOne({ code: req.body.code });
   if (result === null) {
      return res.status(StatusCodes.BAD_REQUEST).json({
         msg: '邀请码已失效',
      });
   }
   const group = await Group.findById(result.groupId);

   if (group === null) {
      return res.status(StatusCodes.BAD_REQUEST).json({
         msg: '该组织已不存在',
      });
   }

   const results = await Group.find({
      _id: result.groupId,
      members: { $in: [req.user.useId] },
   });

   if (results.length !== 0) {
      return res.status(StatusCodes.BAD_REQUEST).json({
         msg: '你已经在该组织',
      });
   }

   const session = await mongoose.startSession();
   session.startTransaction();

   try {
      // 加入分组
      await Group.findByIdAndUpdate(mongoose.Types.ObjectId(group._id), {
         $push: { members: req.user.userId },
      });
      // 发送通知 TODO
      await Notice.insertMany(
         [
            // 1. 记录，给自己看的
            {
               type: 'JOIN_BY_INVITATION_CODE',
               group: group._id,
               receiver: req.user.userId,
            },
            // 2. 给别人反馈
            {
               type: 'INFORMED_SOMEONE_JOIN_BY_INVITATION_CODE',
               group: group._id,
               receiver: group.createdBy,
               sender: req.user.userId,
            },
         ],
         {
            runValidators: true,
         }
      );
      await session.commitTransaction();
      res.status(StatusCodes.OK).json({ msg: '成功' });
   } catch (err) {
      await session.abortTransaction();
      res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({ msg: err });
   } finally {
      session.endSession();
   }
};

module.exports = {
   getGroups,
   createGroup,
   getGroup,
   updateGroup,
   removeGroup,
   genIntiveCode,
   joinGroupByCode,
};
